/** @file bt_init.c
  *  
  * @brief This file contains the major initialization functions
  * in Bluetooth driver.
  * 
  * (c) Copyright 2003-2007, Marvell International Ltd.
  * All Rights Reserved
  */

#include <windows.h>

#include "bt_def.h"
#include "bt_dev.h"
#include "bt_func.h"
#include "If.h"
#include "if_sdio.h"
#include "igxBug.h"
#include "version.h"


VOID MrvSetBtContext( PVOID pBtContext, PVOID pBtIntHandler );
VOID MrvIstBtProc(PVOID pContext, UCHAR ucLHostIntStatus );

/********************************************************
        Local Variables
********************************************************/
// {ED5BDFEC-3BD9-4CC7-BE03-5E9E3B250C69}
static const GUID guidBthSdioIntf = 
{0xed5bdfec, 0x3bd9, 0x4cc7, {0xbe, 0x03, 0x5e, 0x9e, 0x3b, 0x25, 0x0c, 0x69}};

static BOOL isDrvInitialized = FALSE;

#ifndef MRVL_BT_DRV_ONLY
#define SDIO_FUNC1_INIT_COMPLETE_NAME TEXT("SDIO_FUNC1_INIT_COMPLETE_EVENT")
#define SDIO_FUNC2_INIT_COMPLETE_NAME TEXT("SDIO_FUNC2_INIT_COMPLETE_EVENT")
static HANDLE hEventFunc1InitComplete;
static HANDLE hEventFunc2InitComplete;
#endif

/********************************************************
        Global Variables
********************************************************/
ULONG DebugLevel = DBG_LVLDEFAULT;
unsigned long long DebugModule = DBG_MDLDEFAULT;
FILE *MrvDbgfp = NULL;
PMRVDRV_ADAPTER Adapter;

/********************************************************
        Local Functions
********************************************************/
static VOID FreeAdapterResources(PMRVDRV_ADAPTER Adapter)
{
    DeleteCriticalSection(&Adapter->IntCriticalSection);
    DeleteCriticalSection(&Adapter->TxCriticalSection);
    DeleteCriticalSection(&Adapter->RxPacketFreeQLock);
    DeleteCriticalSection(&Adapter->RxPacketPendQLock);

    CloseHandle(Adapter->hControllerInterruptEvent); 
    CloseHandle(Adapter->hThreadReadyEvent);
    CloseHandle(Adapter->hReadPacketEvent);
    CloseHandle(Adapter->hPSAwakeEvent);
    CloseHandle(Adapter->hCmdRespEvent);
    CloseHandle(Adapter->hControllerInterruptThread);
#ifdef INIT_THREAD
    CloseHandle(Adapter->hDelayTimerEvent);
    CloseHandle(Adapter->hInitialThread);
#endif

    return;
}


VOID FreeAdapterObject(PMRVDRV_ADAPTER Adapter)
{
    //Rx Q 
    FreeRxQ(Adapter);

    // System Resource
    FreeAdapterResources(Adapter);

    // Free adapter object itself
    MRVDRV_FREE_MEM((PVOID)Adapter);

    return;
}

MRVL_STATUS AllocateAdapterBuffer(IN PMRVDRV_ADAPTER Adapter)
{
    // Build up RxQ
    if (AllocateRxQ(Adapter) != MRVL_STATUS_SUCCESS)
    {
        FreeAdapterObject(Adapter);
        return MRVL_STATUS_RESOURCES;
    }

    return MRVL_STATUS_SUCCESS;
}


MRVL_STATUS InitSyncObjects(PMRVDRV_ADAPTER Adapter)
{  
    DWORD threadID;  
    DWORD waitStatus;


    InitializeCriticalSection(&Adapter->IntCriticalSection);
    InitializeCriticalSection(&Adapter->TxCriticalSection);

    /* Create Event Object */
    Adapter->hControllerInterruptEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hControllerInterruptEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hControllerInterruptEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    Adapter->hThreadReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hThreadReadyEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hThreadReadyEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    Adapter->hReadPacketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hReadPacketEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hReadPacketEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    Adapter->hPSAwakeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hPSAwakeEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hPSAwakeEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    Adapter->hCmdRespEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hCmdRespEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hCmdRespEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    /* Create IST Thread for SDIO interrupt service */
    Adapter->hControllerInterruptThread = CreateThread(NULL,
                                                       0,
                                                       (LPTHREAD_START_ROUTINE)MrvIstThreadProc,
                                                       Adapter,
                                                       0,
                                                       &threadID);
    if (NULL == Adapter->hControllerInterruptThread)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hControllerInterruptThread failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    DBGPRINT(DBG_LOAD, (L"Wait for IST Thread Ready ..... \n\r"));
    waitStatus = WaitForSingleObject(Adapter->hThreadReadyEvent, INFINITE);
    if (WAIT_OBJECT_0 != waitStatus)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Wait IST Thread Ready failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    }
    DBGPRINT(DBG_LOAD, (L"Wait IST Thread Ready OK\n\r"));

#ifdef INIT_THREAD
    Adapter->hDelayTimerEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (NULL == Adapter->hDelayTimerEvent)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hDelayTimerEvent failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    } 

    /* Create Initial Thread for BT command */
    Adapter->hInitialThread = CreateThread(NULL,
                                           0,
                                           (LPTHREAD_START_ROUTINE)MrvInitThreadProc,
                                           Adapter,
                                           0,
                                           &threadID);
    if (NULL == Adapter->hInitialThread)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Create hInitialThread failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    }

    DBGPRINT(DBG_LOAD, (L"Wait for Initial Thread Ready ..... \n\r"));
    waitStatus = WaitForSingleObject(Adapter->hThreadReadyEvent, INFINITE);
    if (WAIT_OBJECT_0 != waitStatus)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Wait Initial Thread Ready failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    }
    DBGPRINT(DBG_LOAD, (L"Wait Initial Thread Ready OK\n\r"));
#endif

    return MRVL_STATUS_SUCCESS;
}


VOID InitAdapterObject(PMRVDRV_ADAPTER Adapter)
{
    Adapter->SetSD4BIT = 0;     // Enable SDIO 1 bit mode
    Adapter->SdioFastPath = 0;  // Enable Fast Path
    Adapter->SdioIstThreadPriority = 101;

    Adapter->RxHCIAclDataCnt = 0;
    Adapter->RxHCIScoDataCnt = 0;
    Adapter->RxHCIEventPktCnt = 0;
    Adapter->RxVendorPktCnt = 0;
    Adapter->RxUnknownPktCnt = 0;
    Adapter->RxPacketDropCnt = 0;

    Adapter->CardRemoved = FALSE;
    Adapter->bIsOpenConnection = FALSE;

    Adapter->ps_state = PS_AWAKE;
}


/********************************************************
        Global Functions
********************************************************/
MRVL_STATUS MrvBTDrvDeInit(DWORD dwContext)
{
    FreeAdapterObject(Adapter);

    isDrvInitialized = FALSE;

#ifndef MRVL_BUILTIN
    if (!AdvertiseInterface(&guidBthSdioIntf, L"SDIO8688_BT", FALSE))
    {
        RETAILMSG(1, (L"[MRVL-BT]:Failed to AdvertiseInterface\n\r"));
    }
#endif

    return MRVL_STATUS_SUCCESS;
}


MRVL_STATUS MrvBTDrvInit(DWORD dwContext)
{
    PWSTR pRegPath = NULL;
    MRVL_STATUS Status;
#ifdef MRVL_BT_DRV_ONLY
    IF_FW_STATUS fwStatus;
#endif

    DBGPRINT(DBG_LOAD, (L"Enter MrvBTDrvInit\n\r"));

    if (isDrvInitialized == TRUE)
    {
        DBGPRINT(DBG_LOAD, (L"isDrvInitialized = TRUE \n\r"));
        return MRVL_STATUS_FAILURE;
    }
          
#ifndef MRVL_BT_DRV_ONLY
    // Synchronization between WLAN & BT driver 
    // which both will initial SDIO card
    // Use OpenEvent instead of CreateEvent because event has been created by WLAN driver
    while ((hEventFunc1InitComplete = 
            OpenEvent(EVENT_ALL_ACCESS, FALSE,SDIO_FUNC1_INIT_COMPLETE_NAME)) == NULL)
    { 
       DBGPRINT(DBG_LOAD, (L"Waiting Func1 event created....\n\r"));
       Sleep(5000);
    }
    while ((hEventFunc2InitComplete = 
            OpenEvent(EVENT_ALL_ACCESS, FALSE,SDIO_FUNC2_INIT_COMPLETE_NAME)) == NULL)
    { 
       DBGPRINT(DBG_LOAD, (L"Waiting Func2 event created....\n\r"));
       Sleep(5000);
    }
     
    DBGPRINT(DBG_LOAD, (L"Func1 & 2 event opened....\n\r"));
#endif

    // Allocate adapter object space
    Adapter = (PMRVDRV_ADAPTER)MRVDRV_ALLOC_MEM(sizeof(MRVDRV_ADAPTER));
    if (Adapter == NULL)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to allocate adpater buffer !\n\r"));
        return MRVL_STATUS_FAILURE;
    }
    // Zero out the adapter object space
    MrvlZeroMemory(Adapter, sizeof(MRVDRV_ADAPTER));

    // Assign default adapter object value and allocate buffer
    //************************************************************
    InitAdapterObject(Adapter);

    MrvSetBtContext( Adapter, MrvIstBtProc );

    // Acqiure the SD device handle
    Adapter->hDevice = SDGetDeviceHandle((DWORD)dwContext, &pRegPath);
    if (NULL == Adapter->hDevice)
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"SDGetDeviceHandle Failed !\n\r"));
        return MRVL_STATUS_FAILURE;
    }
     
#ifndef MRVL_BT_DRV_ONLY
    DBGPRINT(DBG_LOAD, (L"Wait for hEventFunc1InitComplete event ..... \n\r"));
    WaitForSingleObject(hEventFunc1InitComplete, INFINITE);
    DBGPRINT(DBG_LOAD, (L"hEventSDIOInitComplete event received\n\r"));
#endif

    // Assign default adapter object value and allocate buffer
    //************************************************************
    if (If_Initialize(Adapter, BT_FUNC) != MRVL_STATUS_SUCCESS)
    {
        // Only adapter object itself has been created up to this point
        MRVDRV_FREE_MEM((PVOID)Adapter);
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"SDIO Initialization Failed !\n\r"));
        return MRVL_STATUS_FAILURE;      
    }

    // Allocate synchronization object here
    // Lock for Queue
    InitializeCriticalSection(&Adapter->RxPacketFreeQLock);
    InitializeCriticalSection(&Adapter->RxPacketPendQLock);

    // Allocate Tx/Rx/Cmd related Buffer
    Status = AllocateAdapterBuffer(Adapter);
    if (Status != MRVL_STATUS_SUCCESS)
    {
        // All resources should be freed if allocated faild !
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to allocate memory for Tx/Rx/Cmd !\n\r"));
        return MRVL_STATUS_FAILURE;
    }

    // Initialize object for driver synchronization
    InitSyncObjects(Adapter); 

#ifdef MRVL_BT_DRV_ONLY
    // Check if firmware already downloaded
    if (If_IsFirmwareLoaded(Adapter) == FW_STATUS_INITIALIZED)
    {
        DBGPRINT(DBG_LOAD, (L"Firmware is existed\n\r"));
    }
    else
    {
        // Download Firmware
        fwStatus = If_FirmwareDownload(Adapter);
        if (fwStatus != FW_STATUS_INITIALIZED)
        {
            DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to download the firmware\n\r"));
            return MRVL_STATUS_FAILURE;
        }
    }
#endif

    IF_BUS_CONFIG;

#ifndef MRVL_BT_DRV_ONLY
    if (!hEventFunc2InitComplete)
    {
        DBGPRINT(DBG_LOAD, (L"Event hEventFunc2InitComplete does not exist\n\r"));
        return MRVL_STATUS_FAILURE;
    }
    else
    {
        DBGPRINT(DBG_LOAD, (L"Set hEventFunc2InitComplete event\n\r"));
        SetEvent(hEventFunc2InitComplete);
    }
#endif

    isDrvInitialized = TRUE;

#ifndef MRVL_BUILTIN
    if (!AdvertiseInterface(&guidBthSdioIntf, L"SDIO8688_BT", TRUE))
    {
        DBGPRINT(DBG_LOAD|DBG_ERROR, (L"Failed to AdvertiseInterface !\n\r"));
        return (DWORD)0;
    }
    DBGPRINT(DBG_LOAD, (L"AdvertiseInterface\n\r"));
#endif

    /* Add the Driver Version to Registry */
    {
        REG_VALUE_DESCR Vals[2];

        Vals[0].val_data = (PBYTE)TEXT(MRVDRV_BT_DRIVER_VERSION);
        Vals[0].val_name = TEXT("BTDriverVersion");
        Vals[0].val_type = REG_SZ;
        Vals[1].val_name = NULL;
        AddKeyValues((TEXT("Comm\\") TEXT(IFSTRN) TEXT(CHIPSTRN)), Vals);
    }

    /* Bring up firmware BT Module */
    SendBTCommand(Adapter,
                  CMD_OPTION_NO_WAIT,
                  BT_CMD_MODULE_CFG_REQ,
                  MODULE_BRINGUP_REQ,
                  0);


#ifndef MRVL_BT_DRV_ONLY
    //close handler to prevent card re-plug in fail
    if(hEventFunc1InitComplete != NULL)
       CloseHandle(hEventFunc1InitComplete);
    if(hEventFunc2InitComplete != NULL)
       CloseHandle(hEventFunc2InitComplete);  

    hEventFunc1InitComplete=NULL;
    hEventFunc2InitComplete=NULL;
#endif

    return MRVL_STATUS_SUCCESS;
}


#ifdef INIT_THREAD
VOID MrvInitThreadProc(PVOID pContext)
{
    PMRVDRV_ADAPTER pAdapter = (PMRVDRV_ADAPTER) pContext;
   
    CeSetThreadPriority(GetCurrentThread(), pAdapter->SdioIstThreadPriority); 
    {
        DBGPRINT(DBG_HELP, (L"TestThread priority: %d\n\r",
                           CeGetThreadPriority(GetCurrentThread())));
    }
    SetEvent(pAdapter->hThreadReadyEvent);

#if 0
    while (1)
    {
        DBGPRINT(DBG_CMD, (L"Test Thread running ..... \n\r"));
        if (Adapter->CardRemoved == TRUE)
        {
            DBGPRINT(DBG_CMD|DBG_ERROR, (L"Test Thread terminate due to card removed !\n\r"));
            return;
        }

        WaitForSingleObject(pAdapter->hDelayTimerEvent, 5000);

        /* Send BT Command */
        SendBTCommand(Adapter,
                      CMD_OPTION_WAIT_RESP,
                      BT_CMD_AUTO_SLEEP_MODE,
                      BT_PS_ENABLE,
                      0);
    }
#endif


#if 0

    WaitForSingleObject(pAdapter->hDelayTimerEvent, 3000);

#if 0
    /* Shutdown firmware BT Module first */
    SendBTCommand(Adapter,
                  CMD_OPTION_WAIT_RESP,
                  BT_CMD_MODULE_CFG_REQ,
                  MODULE_SHUTDOWN_REQ,
                  0);

    /* Bring up firmware BT Module */
    SendBTCommand(Adapter,
                  CMD_OPTION_WAIT_RESP,
                  BT_CMD_MODULE_CFG_REQ,
                  MODULE_BRINGUP_REQ,
                  0);
#endif

#if 0
    /* Enable Auto Sleep */
    SendBTCommand(Adapter,
                  CMD_OPTION_WAIT_RESP,
                  BT_CMD_AUTO_SLEEP_MODE,
                  BT_PS_ENABLE,
                  0);
#endif

#if 0
    /* Host Sleep Config */
    SendBTCommand(Adapter,
                  CMD_OPTION_WAIT_RESP,
                  BT_CMD_HOST_SLEEP_CONFIG,
                  0xff,     /* Wakeup by Host Interface */
                  0);

    /* Enable Host Sleep */
    SendBTCommand(Adapter,
                  CMD_OPTION_WAIT_RESP,
                  BT_CMD_HOST_SLEEP_ENABLE,
                  0,
                  0);
#endif

#endif
}
#endif
